AmplifyでCognitoのマネージドログインを利用した認証を最低限のReact実装で動かしてみて動作を理解する

AmplifyでCognitoのマネージドログインを利用した認証を最低限のReact実装で動かしてみて動作を理解する

AmplifyとCognitoを利用すると、Amplifyがうまいことやってくれるので、プログラム開発者は認証フローを意識することなく認証機能が実装できます。 その、うまいことってのが具体的に何をやっているのかを暴きます。
Clock Icon2024.12.16

このブログは、昔書いたこのブログを、現状のライブラリやドキュメント、Cognitoのマネージドログイン機能のUIに合わせて書き直したものです。

https://dev.classmethod.jp/articles/learn-authentication-using-cognitos-hosted-ui-with-amplify/

はじめに

Amplifyを使うと、Cognitoを利用して簡単に認証機能を作れて便利です。

Cognitoにマネージドログイン機能が導入されたことで、より活用しやすくなったと思います。

https://dev.classmethod.jp/articles/amazon-cognito-managed-login/

マネージドログイン機能を利用したログインのイメージ動画を作成したのでこちらをご覧ください。

https://youtu.be/Qxyo7wnVPKQ

実装上の話をすると、ログインUI用の別URLにジャンプする動作をするという関係上、マネージドログイン機能を利用する場合、 認証フローが大きく異なります

その認証フローが大きく変わるという部分を Amplifyがうまいことやってくれて 、 プログラム開発者はほとんど意識することなく認証機能を実装できます。スゴイですね!

Amplifyの公式ドキュメントに、ReactでCognitoのマネージドログイン機能を利用してFacebookログインやGoogleログインを実装するサンプルがあります。 詳しくはこちらを御覧ください。

https://docs.amplify.aws/react/build-a-backend/auth/concepts/external-identity-providers/

動くものを作りたいだけであれば、ここまで読んでいただければ大丈夫です。
ありがとうございました。


ここから先は、「 うまいことってなんなんだよ。全然納得できない。具体的に何をどうしてるんだよ。 」 みたいなことを考えてしまう私みたいなひねくれ者のために、
知らなくても困らないけど、知っているともっと楽しめるかもしれない、
AmplifyとCognitoのマネージドログインがどんな認証フローをとっているのか? そして、 具体的に何をうまいことやってくれているのか? という話をします。

前提

以前のブログは生javascriptで書かれていましたが、今回はReactを使って書き直しています。
このブログではReact自体の説明はしません。Reactについて知りたい方は公式ドキュメントを参照してください。

https://ja.react.dev/

サンプルプログラムを動かす場合、次のコマンドが利用できるようにインストールをしておいてください。

$ node --version
v20.13.1
$ npm --version
10.9.2
$ aws --version
aws-cli/2.22.6 Python/3.12.6 Darwin/23.6.0 exe/x86_64
$ curl --version
curl 8.7.1 (x86_64-apple-darwin23.0) libcurl/8.7.1 (SecureTransport) LibreSSL/3.3.6 zlib/1.2.12 nghttp2/1.61.0

サンプルプログラム置き場

今回作成したプログラムはGitHubで公開しています。 実際に動かしてみたい方はこちらから git clone してください。

https://github.com/rednes/cognito-react-example-for-external-idp

Cognito環境の構築

まずは git clone して環境一式をGitHubから取ってきます。

$ git clone https://github.com/rednes/cognito-react-example-for-external-idp
$ cd cognito-react-example-for-external-idp

次に CloudFormation を利用して、Cognitoの環境を作ります。 次のコマンドを実行すると、Cognitoの環境が作れます。
コマンドが具体的に何をしているか気になる方は、 package.json で定義していますので、詳細はそちらをご覧ください。

$ export AWS_DEFAULT_PROFILE=<<YOUR_AWS_PROFILE>>
$ npm run cfn:deploy

しばらくすると、CloudFormationでCognitoの環境構築が完了します。 次のコマンドで出力結果を確認してください。 次のような形で結果が返ってくれば大丈夫です。

$ npm run cfn:describe
---------------------------------------------------------
|                    DescribeStacks                     |
+------------------------+------------------------------+
|  CognitoRegion         |  ap-northeast-1              |
|  CognitoUserPool       |  ap-northeast-1_XXXXXXXXX    |
|  CognitoUserPoolClient |  XXXXXXXXXXXXXXXXXXXXXXXXXXX |
+------------------------+------------------------------+

次に、ひとつだけマネジメントコンソールで設定してほしい作業があります。

Cognitoがホストしているマネージドログイン用のURLは、Cognitoが用意する画面です。
そのため、アプリケーションとはまったく別ドメインのサイトになります。

このドメインのサブドメインの名前を決めて、Cognitoで設定する必要があります。 サブドメインの設定手順を紹介します。

Cognitoユーザープールのマネジメントコンソール画面を開いてください。
CognitoReactExampleUsers という名前のユーザープールができているので開きます。

image.png

左側のメニューの ドメイン名 を選択して、 アクション > Cognitoドメインの作成 を選択してください。

image.png

ドメインのプレフィックス を入力してください。 ドメインのプレフィックスは後で利用するのでメモしておいてください。
ブランディングバーション は、マネージドログインを選択してください。

image.png

以上でCognitoの環境構築は終了です。

アプリケーションの設定

アプリケーションの設定ファイルを、Cognitoの環境とあわせた値に変更します。

/src/amplifyConfigure.example.ts ファイルを /src/amplifyConfigure.ts にコピーして、さきほどメモしたドメインのプレフィックスを COGNITO_DOMAIN_PREFIX に入力してください。
COGNITO_USER_POOL 等の値は、さきほど実行した npm run cfn:describe コマンドで確認できるのでこれも入力してください。

$ npm run cfn:describe
---------------------------------------------------------
|                    DescribeStacks                     |
+------------------------+------------------------------+
|  CognitoRegion         |  ap-northeast-1              |
|  CognitoUserPool       |  ap-northeast-1_XXXXXXXXX    |
|  CognitoUserPoolClient |  XXXXXXXXXXXXXXXXXXXXXXXXXXX |
+------------------------+------------------------------+
import { ResourcesConfig } from "aws-amplify";

const COGNITO_REGION = "ap-northeast-1";
const COGNITO_USER_POOL = "ap-northeast-1_XXXXXXXXX";
const COGNITO_USER_POOL_CLIENT = "dummy";

const COGNITO_DOMAIN_PREFIX = "dummy";

export const config: ResourcesConfig = {
  Auth: {
    Cognito: {
      userPoolId: COGNITO_USER_POOL,
      userPoolClientId: COGNITO_USER_POOL_CLIENT,
      loginWith: {
        oauth: {
          domain: `${COGNITO_DOMAIN_PREFIX}.auth.${COGNITO_REGION}.amazoncognito.com`,
          scopes: ["openid"],
          redirectSignIn: ["http://localhost:3000/"],
          redirectSignOut: ["http://localhost:3000/"],
          responseType: "code",
        },
      },
    },
  },
};

次に、ローカルでReactアプリケーションを起動するために、次のコマンドを実行してjavascriptパッケージをインストールしてください。

$ npm install

次のコマンドを実行して、待機状態になれば成功です。 http://localhost:3000 でアクセスできるReactアプリケーションができました。

$ npm run dev
> cognito-react-example-for-external-idp@0.0.0 dev
> vite --port 3000


  VITE v6.0.2  ready in 95 ms

  ➜  Local:   http://localhost:3000/

マネージドログインを利用したログインの流れを確認してみる

ブラウザから http://localhost:3000 にアクセスすると、こんな感じのシンプルなボタンがあるだけの画面が表示されます。

image.png

ここで Open Managed Login ボタンをクリックすると、Cognitoのマネージドログイン画面に飛びます。

image.png

ユーザー登録の説明は省いて、すでにユーザーは登録済みという前提で進めます。
Email/Passwordを入力してサインインします。

image-1.png

そうすると、元のページに戻ってユーザー認証が完了し、Cognitoの発行したトークンがベロっと画面に表示されるというアプリケーションです。

image.png

Sign Outボタンを押すと、サインアウトされてトークンの表示もなくなります。

image.png

ただこれだけのシンプルなReactアプリケーションです。

それではコードを読んでいきましょう。 60行程度なので、すぐ読めると思います。

コードの解説

src/App.tsx

https://github.com/rednes/cognito-react-example-for-external-idp/blob/main/src/App.tsx

12行目の Amplify.configure で設定ファイルから、Amplifyの初期設定をしています。

19行目の useEffect で、初回レンダリング時に getCurrentUser , fetchAuthSession を使ってユーザー情報を取得しています。取得できたら useState でその情報を保持しています。

https://docs.amplify.aws/react/build-a-backend/auth/connect-your-frontend/manage-user-sessions/

40行目の onClick で、 Open Managed Loginボンタンが押された時に、 signInWithRedirect を実行するようにしています。
このファンクションが、 Cognitoのマネージドログイン画面へリダイレクトしている処理 の正体です。

52, 53行目で取得してuseStateで保持していたユーザー情報をpreタグでベロっと表示しています。

こんな感じのReactアプリケーションです。

ここで何が言いたいかというと、プログラムを書く際に 明示的に書いてる認証処理 は40行目の signInWithRedirect だけということです。

これ以外ほぼ何もせずに認証処理が実装できています 。スゴイですね!

プログラムが具体的にイメージできたところで、そろそろCognitoの認証フローの話に入っていきます。

Cognitoの認証フロー

CognitoのOAuthフローとして、次の 3つのフロー を利用することが許可されています。

  • Authorization code grant
  • Implicit grant
  • Client credentials

Authorization code grantはPKCEを併用できます。
ですので、4種類あると言ってもよいかもしれません。

  • Authorization code grant
    • PKCE未使用
    • PKCE併用
  • Implicit grant
  • Client credentials

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/federation-endpoints-oauth-grants.html

本筋から外れるので、本ブログでは 各フローについての詳細までは立ち入りません
どういう場合にどのフローを選択するかだけざっと説明します。

  • Authorization code grant(基本的にこのフローが推奨)
    • PKCE未使用(Webアプリケーション等、トークンをサーバが仲介して取得する場合。通常クライアントシークレットを設定する)
    • PKCE併用(SPAやモバイルアプリ等、トークンをユーザーが直接取得する場合)
  • Implicit grant(Authorization code grantが不可能な場合選択。PKCE併用したcode grantが推奨される)
  • Client credentials(エンドユーザーが認証に使うフローではない)

各フローはOAuth 2.0で定義されている認可フローとほぼ同じものですので、各フローの詳細はOAuth2.0認可フローをご参照ください。参考資料をブログの最後に貼っておきます。

今回のサンプルアプリケーションは、 トークンをユーザーJavaScriptを使って直接取得 しておりSPAに相当します。
よって、推奨されている PKCEを併用するAuthorization code grant 決め打ちで説明します。

それでは、 PKCEを併用するAuthorization code grant フローとは何なのでしょうか?

PKCEを使用しないAuthorization code grant

いきなりPKCEを併用するAuthorization code grantの話をすると混乱するので、 いったんPKCEを使用しない場合のフローについて説明します。

本ブログでは、OAuth2.0の認可フローである一般的なAuthorization code grantの話というよりは、
CognitoのAuthorization code grantフローがどういう動きをしているのかという具体的な動きを説明します。

まずは、Authorization code grantフローの全体像をシーケンス図で示します。

amplify2

Authorization code grant フローは、次のステップで進んでいきます。

ユーザーがアプリケーションに対してログイン要求を出すと、 アプリケーションはCognitoの 認可エンドポイント に対して認証リクエストを投げるための、リダイレクトレスポンスをユーザーに返します。(フロー図の01〜02)

そうすると、ユーザーは認可エンドポイントにリダイレクトされ、Cognitoに認証リクエストを投げることになります。 Cognitoはそれに応じてリダイレクトレスポンスで ログインエンドポイント(ログイン画面のURL) を返します。(フロー図の03〜04)

リダイレクトレスポンスが返ってくるので、ユーザーはログイン画面にリダイレクトされます。 ログイン画面が表示されるので、ユーザーはID/Passwordを入力してユーザーを認証します。(フロー図の05〜07)

そうすると、Cognitoは 認可コード を発行して、リダイレクトレスポンスのURLクエリパラメーターに認可コードを添えてユーザーに返します。(フロー図の08)

リダイレクトレスポンスが返ってくるので、ユーザーは認可コードつきでアプリケーションのCallback URLへリダイレクトされます。(フロー図の09)

Callback URLのアクセスで認可コードを受け取ったアプリケーションは、その認可コードを使ってCognitoの トークンエンドポイント に対してトークンリクエストを投げます。(フロー図の11)

Cognitoはその認可コードが有効であると判断すれば、 IDトークン,アクセストークン,リフレッシュトークン をアプリケーションに返します。(フロー図の12)

アプリケーションはIDトークンの検証をして問題なければ、ログイン完了したことをユーザーに伝えます。(フロー図の13〜14)

これが、 Authorization code grant フローの流れです。


これを読んで、 Authorization code grant のイメージ、湧きましたでしょうか?

私はこの説明を文章で読んだけでは、さっぱりわかりませんでした。

具体的にCognitoに対して出しているリクエストを自分の手で実行してみると理解が進むと思います。
ですので、実際にシェルとブラウザで Authorization code grant フローを体験してみましょう。

シェルとブラウザでAuthorization code grantをやってみる

Authorization code grant フローのシーケンス図を再掲します。

amplify2

まずは、認証リクエストをCognitoの認可エンドポイントに送り、リダイレクトレスポンスを受け取るところをやってみます。(フロー図の03〜04)

Cognitoの認可エンドポイントについての詳細は、こちらのドキュメントをご参照ください。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/authorization-endpoint.html

リクエストの送信先とするCognitoは、先ほどのサンプルアプリケーション用に構築したものを利用します。
そして、ドメイン名はログイン画面のURLのドメイン名と同じです。

つまり、ドメインプレフィックスを COGNITO_DOMAIN_PREFIX として、リージョンを ap-northeast-1 で構築していた場合、
認可エンドポイントのURLは https://COGNITO_DOMAIN_PREFIX.auth.ap-northeast-1.amazoncognito.com/oauth2/authorize になります。

よって、次のように curl コマンドを実行することで、この認可エンドポイントに対して認証リクエストを投げることができます。

$ COGNITO_REGION=ap-northeast-1
$ COGNITO_USER_POOL_CLIENT=<<YOUR_COGNITO_CLIENT_ID>>
$ COGNITO_DOMAIN_PREFIX=<<YOUR_COGNITO_DOMAIN_PREFIX>>
$ COGNITO_DOMAIN=${COGNITO_DOMAIN_PREFIX}.auth.${COGNITO_REGION}.amazoncognito.com
$ curl -i "https://${COGNITO_DOMAIN}/oauth2/authorize?response_type=code&client_id=${COGNITO_USER_POOL_CLIENT}&redirect_uri=http://localhost:3000&state=xyz&scope=openid"

クエリパラメーターの項目と設定値について説明します。

  • response_type
    • code または token を指定します。認可コードを要求するAuthorization code grantの場合、 code を指定します。よって、今回は code を指定します。
  • client_id
    • CognitoユーザープールのクライアントIDを指定します。
  • redirect_uri
    • 認可コードを返すリダイレクト先のURLを指定します。今回は、 http://localhost:3000 を指定します。
  • state
    • Cognitoがクライアントにリダイレクトして戻るときに、この値を含めるようになります。オプション値ですが、CSRF攻撃を防ぐために設定することが強く推奨されています。
  • scope
    • スコープを指定します。OIDC認証をする場合、 openid の指定が必須です。今回は opeind を指定します。

実際にこのリクエストを投げてみましょう。

そうすると、 302リダイレクト のレスポンスが返ってきて、 Locationレスポンスヘッダー にリダイレクト先のURLが示されていることがわかります。

HTTP/2 302
location: https://<<YOUR_COGNITO_DOMAIN_PREFIX>>.auth.ap-northeast-1.amazoncognito.com/login?client_id=h10utk89qfrujrmc1h2lep8bd&redirect_uri=http://localhost:3000&response_type=code&scope=openid&state=xyz

また、LocationのURLから、リダイレクト先はログインエンドポイントであることがわかります。
このログインエンドポイントというのが、いわゆるCognitoの マネージドログイン画面のURL のことです。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/login-endpoint.html

次にこのリダイレクト先のURLへブラウザを使ってアクセスしてみます。(フロー図の05〜08)

そうするとこのようにCognitoのログイン画面が表示されます。
ログインするとlocalhostへリダイレクトされてくるので、ローカルサーバーがリクエストを受け取らないように Reactアプリケーションは終了 しておいてください。

image.png

E-mail/Passwordを入力してログインすると、Cognitoからリダイレクトレスポンスが返ってきます。

image-1.png

リダイレクトレスポンスは次のような形をしています。

http://localhost:3000/?code=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&state=xyz

state には認証リクエストで指定したxyzが入っています。 通常CSRF攻撃を防ぐため、認証リクエストで送信した state をアプリケーション側で保持しておきます。そして、リダイレクトで返ってきた state と一致しているかをアプリケーション側でチェックします。
code の値に入っているものが 認可コード です。 認可コード はトークンリクエストの際に必要です。

次に IDトークンアクセストークン の取得処理です。
トークンエンドポイントに対してトークンリクエストをやってみます。(フロー図の11〜12)

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/token-endpoint.html

Callbackで受け取った 認可コードAUTHORIZATION_CODE 環境変数に格納すると、
次のような curl コマンドを実行することで、トークンエンドポイントに対してトークンリクエストを投げることができます。

$ AUTHORIZATION_CODE=<<YOUR_AUTHORIZATION_CODE>>
$ COGNITO_REGION=ap-northeast-1
$ COGNITO_USER_POOL_CLIENT=<<YOUR_COGNITO_CLIENT_ID>>
$ COGNITO_DOMAIN_PREFIX=<<YOUR_COGNITO_DOMAIN_PREFIX>>
$ COGNITO_DOMAIN=${COGNITO_DOMAIN_PREFIX}.auth.${COGNITO_REGION}.amazoncognito.com
$ curl -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "client_id=${COGNITO_USER_POOL_CLIENT}" \
  -d "scope=openid" \
  -d "redirect_uri=http://localhost:3000" \
  -d "code=${AUTHORIZATION_CODE}" \
  "https://${COGNITO_DOMAIN}/oauth2/token"

パラメーターの項目と設定値について説明します。

  • grant_type
    • authorization_code , refresh_token または client_credentials を指定します。今回はAuthorization code grantに沿ってトークンを要求しているので、 authorization_code を指定します。
  • client_id
    • CognitoユーザープールのクライアントIDを指定します。
  • scope
    • スコープを指定します。OIDC認証をする場合、 openid の指定が必須です。今回は opeind を指定します。
  • redirect_uri
    • 認可コードを要求したときに指定した redirect_uri と同じ値を設定する必要があります。今回は、 http://localhost:3000 を指定します。
  • code
    • 認可コードを指定します。

トークンリクエストが成功すると、トークンレスポンスが次のような形で返ってきます。

{
    "id_token": "eyJraWQ...5peOLw",
    "access_token": "eyJ...NISbew",
    "refresh_token": "ey...Kz7AnA",
    "expires_in": 3600,
    "token_type": "Bearer"
}

この、 id_token の値が IDトークン で、 access_token の値が アクセストークン です。

これで、Cognitoからトークンが取得できてユーザーのログインが完了しました。

最後に、この IDトークン の中身を見てましょう。 IDトークンJWT形式 でユーザーに関する情報が入っています。

そのため、jwt.io等のサイトやライブラリを使って中身を確認できます。

https://jwt.io/

jwt.ioにIDトークンをコピー&ペーストしてみると、このように中に入っているユーザー情報を確認できます。

image

PKCEを併用するAuthorization code grant

ここまで PKCEを使用しないAuthorization code grant の話をしてきました。

PKCEを併用する場合も フロー自体は変わりません
認可コードの横取り攻撃の対策のために、随所でパラメーターのやりとりが追加されます。

OAuth2.0のPKCEを理解するには、ゲストブロガーとしてAuth0社の筒井さんに書いていただいたこのブログがわかりやすいです。

https://dev.classmethod.jp/articles/oauth-2-0-pkce-by-auth0/

PKCEの一般的な話はこちらのブログに説明をゆずり、本ブログではCognitoの具体的な動きを見ていきます。

PKCEを併用するAuthorization code grant フローの全体像をシーケンス図で示します。

amplify3

PKCEを使用しないAuthorization code grant フローから追加された、赤字で書いた処理だけを説明します。

まず認証リクエストを送る前に、アプリケーション側で code_verifier の発行と保存をし、 code_verifiercode_challenge_method に沿った方法で code_challenge に変換します。(フロー図の02)

ちなみに、Cognitoが対応している code_challenge_method は現段階では S256 のみです。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/using-pkce-in-authorization-code.html

その後、パラメーターに code_challengecode_challenge_method を追加して認可エンドポイントに認証リクエストを投げるための、リダイレクトレスポンスをユーザーに返します。(フロー図の03)

そうすると、ユーザーは認可エンドポイントにリダイレクトされ、 code_challenge , code_challenge_method パラメーターとあわせて、Cognitoに認証リクエストを投げることになります。
Cognitoはそれに応じてリダイレクトレスポンスで ログインエンドポイント(ログイン画面のURL) を返します。(フロー図の04〜05)

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/authorization-endpoint.html

その後は、トークンリクエストする直前まで飛んで、Callback URL経由で認可コードを受け取った後、保存していた code_verifier の読込と破棄をします。(フロー図の12)

その後、パラメーターに code_verifier を追加してトークンエンドポイントにトークンリクエストを投げます。(フロー図の13)

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/token-endpoint.html

これが、 PKCEを併用したAuthorization code grant フローです。

PKCEを併用したAuthorization code grant フローでの認証を採用する場合、アプリケーション側はこのフローに沿ってCognitoとパラメーターをやりとりする実装が必要です。

Amplifyも当然、このフローに沿って認証処理をしています。
Cognitoの認証フローが理解できたところで、いよいよAmplifyがうまいことやってくれていることを暴いていきます。

Amplifyがうまいことやってくれていること

Amplifyの設定

まず、Amplifyがうまいことやるための設定についてです。
Amplify.configure の引数としているこの値が重要です。

この設定で、AmplifyとCognitoがどの認証フローでやりとりするのか、どのCognitoのエンドポインと通信したいのか、リダイレクト時のコールバックURLが何なのかを定義しています。

export const config: ResourcesConfig = {
  Auth: {
    Cognito: {
      userPoolId: COGNITO_USER_POOL,
      userPoolClientId: COGNITO_USER_POOL_CLIENT,
      loginWith: {
        oauth: {
          domain: `${COGNITO_DOMAIN_PREFIX}.auth.${COGNITO_REGION}.amazoncognito.com`,
          scopes: ["openid"],
          redirectSignIn: ["http://localhost:3000"],
          redirectSignOut: ["http://localhost:3000"],
          responseType: "code",
        },
      },
    },
  },
};

https://docs.amplify.aws/react/build-a-backend/auth/use-existing-cognito-resources/

特に重要なパラメーターの項目と設定値について説明します。

  • Auth.Cognito.userPoolClientId
    • CognitoユーザープールのクライアントIDを指定します。認証リクエストやトークンリクエストする際に、クライアントIDとして利用されます。
  • Auth.Cognito.loginWith.oauth.domin
    • 認可エンドポイント等、各エンドポイントにアクセスする際のCognitoのエンドポイントのドメイン名として利用されます。
  • Auth.Cognito.loginWith.oauth.scopes
    • スコープを指定します。OIDC認証をする場合、 openid の指定が必須です。今回は opeind を指定しています。
  • Auth.Cognito.loginWith.oauth.redirectSignIn
    • 認証リクエストやトークンリクエストをする際のリダイレクト先URLを指定します。今回は、 http://localhost:3000 を指定しています。
  • Auth.Cognito.loginWith.oauth.responseType
    • code または token を指定します。Authorzation code grantフローを採用する場合、 code を指定します。Implicit grantフローを採用する場合、 token を指定します。今回は、Authorzation code grantフローを採用したいので、 code を指定します。

ちなみに、Auth.Cognito.loginWith.oauth.redirectSignIn はCognitoのアプリケーションクライアントのマネージドログインページ編集画面で設定する コールバックURL と一致していないと、Cognito側が受け付けてくれません。

また、 Auth.Cognito.loginWith.oauth.responseTypecode を指定する場合は、 Authorzation code grant フローを採用することになります。
その場合、Cognitoのこの画面で Authorization code grant(認証コード付与) を指定する必要があります。
token を指定する場合は、 Implicit grant フローを採用することにります。その場合、 Implicit grant(暗黙的な付与) を指定する必要があります。

また、 Auth.Cognito.loginWith.oauth.scopes で指定できるスコープは、Cognitoのこの画面で許可されている OpenID Connect のスコープ に限ります。

image-1.png

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-settings-client-apps.html

Amplifyの動作

PKCEを併用するAuthorization code grant フローのシーケンス図を再掲します。

amplify3

Amplifyが何をうまいことやっているのか明らかにするために、
先ほど構築したサンプルアプリケーションとブラウザの開発者ツールを使ってAmplifyの動きを追ってみます。

signInWithRedirect にブレークポイントをしかけた上で、Open Managed Loginボタンをおして signInWithRedirect を実行し、ステップインで処理を追っていきます。

image-1.png

そうすると、ローカルストレージに statecode_verifier を保存している動きが見えます。(フロー図の02)

image-1.png

その後、ブレークから復帰してCognitoのマネージドログイン画面まで遷移すると、Cognitoの認可エンドポイントに対して、 state , code_challenge , code_challenge_method 等をパラメーターとして認証リクエストをしている動きが見えます。(フロー図の03〜04)

image-1.png

その後、認可エンドポイントからリダイレクトレスポンスを受け取り、 ログインエンドポイントへリダイレクト している動きが見えます。(フロー図の05〜06)

image.png

つまり、 signInWithRedirect(&ブラウザのリダイレクト)が次のような処理を開発者が気にしなくていいように隠蔽してくれています。

amplify4

そして、認証フローを理解していると次の疑問が出てきます。
「それじゃあ、 トークンリクエストはいったいどこの誰がやっているのだろう?

これは、Amplifyのソースを追っていくとわかります。 Amplify.configure がやってくれています。

https://github.com/aws-amplify/amplify-js

これも、サンプルアプリケーションとブラウザの開発者ツールを使ってAmplifyの動きを追ってみます。

ログイン前にあらかじめ Amplify.configure へブレークポイントを設定しておきます。

image.png

ブレークポイントを設定したうえで、ログインしてリダイレクトで戻ってくると、 Amplify.configure の実行時に止まります。

image.png

この時点では、URLに 認可コード も含まれているし、ローカルストレージに statecode_verifier も残っています。

image.png

そして Amplify.configure からステップインで処理を追っていくと、ローカルストレージから statecode_verifier が削除されている動きが見えます。(フロー図の12)

image.png

そして、Cognitoトークンエンドポイントに対して、 code , code_verifier 等をパラメーターとして トークンリクエスト が投げられて。(フロー図の13)

image.png

トークンレスポンスを受け取るという動きも見えます。(フロー図の14)

image.png

ブレークから復帰して最後まで進めると、最終的にはローカルストレージに IDトークンアクセストークン が保存されていることがわかります。

image.png

つまり、 Amplify.configure(&ブラウザのリダイレクト)が次のような処理を開発者が気にしなくていいように隠蔽してくれています。

amplify5

結論として、これまで説明したような認証フローの処理を Amplifyがうまいことやってくれる ので、 プログラム開発者は認証フローをほとんど意識することなく、 signInWithRedirect するだけで認証機能を実装できるというわけです。 スゴイですね。

おわりに

知らなくても困らないけど知っているとおもしろいかもしれない Amplify の認証の裏話について語りました。

実際この認証フローのアプリケーション側を自分で実装しようとするととても面倒くさいので、 Amplify が隠蔽してうまいことやってくれるのはとてもうれしいですね。

認証部分はぜひAmplify等のライブラリを使って楽をしましょう。

参考資料

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.